/**
 * 
 */
package gpl.scotlandyard.fugitivepathfinder;

import gpl.scotlandyard.beans.MarkedNode;
import gpl.scotlandyard.beans.basics.Link;
import gpl.scotlandyard.beans.basics.Node;
import gpl.scotlandyard.beans.basics.Player;
import gpl.scotlandyard.beans.basics.Ticket;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

/** @author Norbert */
public class MyTracker extends Player.ListenerImpl {

  private static final Logger LOG = Logger.getLogger(MyTracker.class);

  private final Player trackedPlayer;

  private List<MarkedNode> possiblePositions = new ArrayList<MarkedNode>();

  /**
   * 
   */
  public MyTracker(Player player) {
    this.trackedPlayer = player;
    trackedPlayer.addListener(this);
  }

  /** @param ticket */
  public void movesWithTicket(Ticket ticket) {
    // search all possible
    ArrayList<MarkedNode> futurePositions = new ArrayList<MarkedNode>();
    for (MarkedNode srcNode : possiblePositions) {
      for (Link nodeLink : srcNode.getLinks()) {
        if (nodeLink.getTicket() == ticket || ticket == Ticket.HIDE) {
          MarkedNode markedNode = new MarkedNode(nodeLink.getToNode());
          markedNode.setMark(srcNode.getMark());
          if (!futurePositions.contains(markedNode)) {
            futurePositions.add(markedNode);
            LOG.debug("future node added : " + markedNode);
          } else { // if it's already a future possible node : choose better mark
            Iterator<MarkedNode> it = futurePositions.iterator();
            MarkedNode next = null;
            while (it.hasNext()) {
              next = it.next();
              if (next.equals(markedNode)) {
                break;
              }
            }
            if (next.getMark() < markedNode.getMark()) {
              futurePositions.remove(next);
              futurePositions.add(markedNode);
              LOG.debug("future node corrected with better color : " + markedNode);
            }
          }
        }
      }
    }
    possiblePositions = futurePositions;
    Collections.sort(possiblePositions, MarkedNode.getMarkNumberComparator());
  }

  /** @return */
  public List<MarkedNode> getPossiblePositions() {
    return Collections.unmodifiableList(possiblePositions);
  }

  public void setPossiblePositions(List<Node> positions) {
    possiblePositions.clear();
    for (Node n : positions) {
      possiblePositions.add(new MarkedNode(n));
    }
  }

  /** @param node */
  public void setKnownPosition(Node node) {
    possiblePositions.clear();
    LOG.info(trackedPlayer.getName() + " known position " + node.getNumber());
    possiblePositions.add(new MarkedNode(node));
  }

  /** @param node */
  public void setKnownImpossiblePosition(Node node) {
    if (possiblePositions.remove(new MarkedNode(node))) {
      LOG.info(trackedPlayer.getName() + " remove known impossible node " + node.getNumber());
    } else {
      LOG.warn(trackedPlayer.getName() + " can't remove known impossible node " + node.getNumber());
    }
  }

  /**
   * @param position
   * @param mark
   */
  public void changeMark(int position, int mark) {
    if (mark == MarkedNode.IMPOSSIBLE_MARK) {
      setKnownImpossiblePosition(possiblePositions.get(position).getNode());
    } else {
      possiblePositions.get(position).setMark(mark);
    }
    Collections.sort(possiblePositions, MarkedNode.getMarkNumberComparator());
  }

  @Override
  public void playerTicketsUpdated(Player player) {
    // do nothing for the moment
  }

  @Override
  public void playerMoved(Player player, Link link) {
    // do nothing for the moment
  }

  /**
   * @return trackedPlayer
   */
  public Player getPlayer() {
    return trackedPlayer;
  }

}
